(***************************************************************************************************
 * derlang - the erlang in delphi project.
 *
 * erlang driver interface transition, module 'erl_driver.h'
 *
 * author: aimingoo
 * mail: aiming@gmail.com
 * site: http://code.google.com/p/derlang/
 * date:  23:00 2008-12-12
 **************************************************************************************************)

{$DEFINE __ERL_DRIVER_H__}

{$DEFINE USE_REAL_DRIVER_NAME}

{$DEFINE _OSE_}

{ $DEFINE HAVE_CONFIG_H}         // skip the head file
{ $DEFINE ERL_SYS_DRV}           // can't use
{ $DEFINE STATIC_ERLANG_DRIVER}  // can't use

{$IFDEF HAVE_CONFIG_H}
  {$I CONFIG_H.INC}
{$ENDIF}

(* Windows dynamic drivers, everything is different... *)
{$IFNDEF STATIC_ERLANG_DRIVER}
  {$DEFINE ERL_DRIVER_TYPES_ONLY}
  {$DEFINE WIN32_DYNAMIC_ERL_DRIVER}
{$ENDIF}

unit Erl_Driver;

interface

(*
 * This structure can be cast to a WSABUF structure.
 *)
type
  SysIOVec = packed record // _SysIOVec
    iov_len: Cardinal;
    iov_base: PAnsiChar;
  end;
  PSysIOVec = ^SysIOVec;

const
  DO_READ  = 1 shr 0;
  DO_WRITE = 1 shr 1;
{$IFDEF _OSE_}
  DO_START = 1 shr 2;
  DO_STOP  = 1 shr 3;
{$ENDIF}

const
  ERL_DRV_EXTENDED_MARKER         = $feeeeeed;
  ERL_DRV_EXTENDED_MAJOR_VERSION  = 1;
  ERL_DRV_EXTENDED_MINOR_VERSION  = 2;

(*
 * The emulator will refuse to load a driver with different major
 * version than the one used by the emulator.
 *)

const
  // Values for set_port_control_flags()
  PORT_CONTROL_FLAG_BINARY      = 1 shr 0;
  PORT_CONTROL_FLAG_HEAVY       = 1 shr 1;

  // Values for get_port_flags() *)
  PORT_FLAG_BINARY              = 1 shr 0;
  PORT_FLAG_LINE                = 1 shr 1;

  ERL_DRV_FLAG_USE_PORT_LOCKING = 1 shr 0;


(*
 * unknow types, it's free or undefined.
 * ???
 *)
type
  _erl_drv_data  = LONGINT;
  _erl_drv_event = LONGINT;
  _erl_drv_port  = LONGINT;
  ErlDrvTid_     = LONGINT;
  ErlDrvMutex_   = LONGINT;
  ErlDrvCond_    = LONGINT;
  ErlDrvRWLock_  = LONGINT;
  erl_drv_port_data_lock  = LONGINT;
  SysDriverOpts  = LONGINT;

type
  ErlDrvTid    = ^ErlDrvTid_;
  ErlDrvMutex  = ErlDrvMutex_;
  ErlDrvCond   = ErlDrvCond_;
  ErlDrvRWLock = ErlDrvRWLock_;
  ErlDrvTSDKey = Integer;
  ErlDrvPDL    = ^erl_drv_port_data_lock;

  PErlDrvTid     = ^ErlDrvTid;
  PErlDrvMutex   = ^ErlDrvMutex;
  PErlDrvCond    = ^ErlDrvCond;
  PErlDrvRWLock  = ^ErlDrvRWLock;
  PErlDrvTSDKey  = ^ErlDrvTSDKey;
  PErlDrvPDL     = ^ErlDrvPDL;

  PSysDriverOpts = ^SysDriverOpts;

(*
 * type alias
 *)
type
  size_t         = LongInt;  // or Cardinal.
  p_size_t       = ^size_t;

(*
 * A binary as seen in a driver. Note that a binary should never be
 * altered by the driver when it has been sent to Erlang.
 *)
type
  ErlDrvBinary = packed record // erl_drv_binary
      orig_size: LongInt;  (* total length of binary *)
      orig_bytes: array [0..0] of AnsiChar;  (* the data (char instead of byte!) *)
  end;
  PErlDrvBinary = ^ErlDrvBinary;

  erl_drv_event_data = packed record
    events: ShortInt;
    revents: ShortInt;
  end;

(*
 * Note: These types are incomplete to catch type errors easier.
 *)
type
{$IFNDEF ERL_SYS_DRV}
  ErlDrvEvent = ^_erl_drv_event; // An event to be selected on.
  ErlDrvPort  = ^_erl_drv_port;  // A port descriptor.
{$ENDIF}
  ErlDrvThreadData = ^_erl_drv_port;      // Thread data.
  ErlDrvEventData  = ^erl_drv_event_data; // Event data

(*
 * Used in monitors...
 *)
  ErlDrvTermData = Cardinal;
  ErlDrvUInt = Cardinal;
  ErlDrvSInt = Integer;
  PErlDrvTermData = ^ErlDrvTermData;

  // A driver monitor
  ErlDrvMonitor = packed record
    data: Array [0..sizeof(pointer)*4-1] of BYTE;
  end;
  PErlDrvMonitor = ^ErlDrvMonitor;

  // System info
  ErlDrvSysInfo = packed record
    driver_major_version: Integer;
    driver_minor_version: Integer;
    erts_version: PAnsiChar;
    otp_release: PAnsiChar;
    thread_support: Integer;
    smp_support: Integer;
    async_threads: Integer;
    scheduler_threads: Integer;
  end;
  PErlDrvSysInfo = ^ErlDrvSysInfo;

  ErlDrvNowData = packed record
    megasecs: Cardinal;
    secs: Cardinal;
    microsecs: Cardinal;
  end;
  PErlDrvNowData = ^ErlDrvNowData;

type
  ErlIOVec = packed record // erl_io_vec
      vsize: Integer;      (* length of vectors *)
      size: Integer;       (* total size in bytes *)
      iov: ^SysIOVec;
      binv: ^PErlDrvBinary;
  end;
  PErlIOVec = ^ErlIOVec;

(*
 * erl driver thread types
 *)
type
  ErlDrvData  = ^_erl_drv_data;  // Data to be used by the driver itself.

  ErlDrvThreadOpts = packed record
    suggested_stack_size: Integer;
  end;
  PErlDrvThreadOpts = ^ErlDrvThreadOpts;

(*
 * function/procedure pointer for ErlDrvData types's member.
 *)
  // called at system start up for statically linked drivers, and after loading for
  // dynamically loaded drivers
  TDrvEntry_init         = function : integer; cdecl;

{$IFNDEF ERL_SYS_DRV}
  // called when open_port/2 is invoked. return value -1 means failure.
  TDrvEntry_start        = function (port: ErlDrvPort; command: PAnsiChar): ErlDrvData; cdecl;
{$ELSE}
  // special options, only for system driver
  TDrvEntry_start        = function (port: ErlDrvPort; command: PAnsiChar; opts: PSysDriverOpts): ErlDrvData; cdecl;
{$ENDIF}

  // called when port is closed, and when the emulator is halted.
  TDrvEntry_stop         = procedure(drv_data: ErlDrvData); cdecl;

  // called when we have output from erlang to the port
  TDrvEntry_output       = procedure(drv_data: ErlDrvData; buf: PAnsiChar; len: Integer); cdecl;

  // called when we have input from one of the driver's handles
  TDrvEntry_ready_input  = procedure(drv_data: ErlDrvData; event: ErlDrvEvent ); cdecl;

  // called when output is possible to one of the driver's handles
  TDrvEntry_ready_output = procedure(drv_data: ErlDrvData; event: ErlDrvEvent ); cdecl;

  // called before unloading the driver - DYNAMIC DRIVERS ONLY
  TDrvEntry_finish       = procedure; cdecl;

  // "ioctl" for drivers - invoked by port_control/3
  TDrvEntry_control      = function (drv_data: ErlDrvData; command: Cardinal; buf: PAnsiChar; len: Integer; rbuf: PPAnsiChar; rlen: Integer): Integer; cdecl;

  // Handling of timeout in driver
  TDrvEntry_timeout      = procedure(drv_data: ErlDrvData); cdecl;

  // called when we have output from erlang to the port
  TDrvEntry_outputv      = procedure(drv_data: ErlDrvData; ev: PErlIOVec); cdecl;

  // ...
  TDrvEntry_ready_async  = procedure(drv_data: ErlDrvData; thread_data: ErlDrvThreadData ); cdecl;

  // called when the port is about to be closed, and there is data in the driver queue that needs
  // to be flushed before 'stop' can be called
  TDrvEntry_flush        = procedure(drv_data: ErlDrvData); cdecl;

  // Works mostly like 'control', a syncronous call into the driver.
  TDrvEntry_call         = function (drv_data: ErlDrvData; command: Cardinal; buf: PAnsiChar; len: Integer; rbuf: PPAnsiChar; rlen: Integer; flags: PInteger): Integer; cdecl;

  // Called when an event selected by driver_event() has occurred
  TDrvEntry_event        = procedure(drv_data: ErlDrvData; event: ErlDrvEvent; event_data: ErlDrvEventData); cdecl;

  // Called when a process monitor fires
  TDrvEntry_process_exit = procedure(drv_data: ErlDrvData; monitor: PErlDrvMonitor); cdecl;

(*
 * This structure defines a driver.
 *)
  _erl_drv_entry  = packed record
    init            : TDrvEntry_init;
    start           : TDrvEntry_start;
    stop            : TDrvEntry_stop;
    output          : TDrvEntry_output;
    ready_input     : TDrvEntry_ready_input;
    ready_output    : TDrvEntry_ready_output;
    driver_name     : PAnsiChar;  (* name supplied as command in open_port XXX ? *)
    finish          : TDrvEntry_finish;
    handle          : pointer;    (* Reserved -- Used by emulator internally *)
    control         : TDrvEntry_control;
    timeout         : TDrvEntry_timeout;
    outputv         : TDrvEntry_outputv;
    ready_async     : TDrvEntry_ready_async;
    flush           : TDrvEntry_flush;
    call            : TDrvEntry_call;
    event           : TDrvEntry_event;
    extended_marker : Integer;    (* ERL_DRV_EXTENDED_MARKER *)
    major_version   : Integer;    (* ERL_DRV_EXTENDED_MAJOR_VERSION *)
    minor_version   : Integer;    (* ERL_DRV_EXTENDED_MINOR_VERSION *)
    driver_flags    : Integer;    (* ERL_DRV_FLAGs *)
    handle2         : pointer;    (* Reserved -- Used by emulator internally *)
    process_exit    : TDrvEntry_process_exit;
    (* When adding entries here, dont forget to pad in obsolete/driver.h *)
  end;
  ErlDrvEntry = _erl_drv_entry;
  PErlDrvEntry = ^ErlDrvEntry;

(*
 * Error codes that can be return from driver.
 *)
const
  // Exception code from open_port/2 will be {'EXIT',{einval,Where}}.
  ERL_DRV_ERROR_GENERAL : ErlDrvData = POINTER(-1);

  // Exception code from open_port/2 will be {'EXIT',{Errno,Where}},
  // where Errno is a textual representation of the errno variable
  // (e.g. eacces if errno is EACCES).
  ERL_DRV_ERROR_ERRNO : ErlDrvData = POINTER(-2);

  // Exception code from open_port/2 will be {'EXIT',{badarg,Where}}.
  ERL_DRV_ERROR_BADARG : ErlDrvData = POINTER(-3);


(* Constants for return flags from the 'port_call' callback *)
const
  DRIVER_CALL_KEEP_BUFFER = $00000001;

(* ErlDrvTerm is the type to use for casts when building
 * terms that should be sent to connected process,
 * for instance a tuple on the form {tcp, Port, [Tag|Binary]}
 *
 * ErlDrvTerm spec[] = {
 *    ERL_DRV_ATOM, driver_mk_atom("tcp"),
 *    ERL_DRV_PORT, driver_mk_port(drv->ix),
 *             ERL_DRV_INT, REPLY_TAG,
 *             ERL_DRV_BINARY, (ErlDrvTerm)bin, 50, 0,
 *             ERL_DRV_LIST, 2,
 *    ERL_DRV_TUPLE, 3,
 *  }
 *
 *)
type
  TERM_DATA = ErlDrvTermData;

(* Possible types to send from driver        Argument type *)
const
  ERL_DRV_NIL         : ErlDrvTermData = 1;  // None
  ERL_DRV_ATOM        : ErlDrvTermData = 2;  // driver_mk_atom(string)
  ERL_DRV_INT         : ErlDrvTermData = 3;  // ErlDrvSInt
  ERL_DRV_PORT        : ErlDrvTermData = 4;  // driver_mk_port(ix)
  ERL_DRV_BINARY      : ErlDrvTermData = 5;  // ErlDrvBinary*, * ErlDrvUInt size, * ErlDrvUInt offs
  ERL_DRV_STRING      : ErlDrvTermData = 6;  // char*, ErlDrvUInt
  ERL_DRV_TUPLE       : ErlDrvTermData = 7;  // ErlDrvUInt
  ERL_DRV_LIST        : ErlDrvTermData = 8;  // ErlDrvUInt
  ERL_DRV_STRING_CONS : ErlDrvTermData = 9;  // char*, ErlDrvUInt
  ERL_DRV_PID         : ErlDrvTermData = 10; // driver_connected,...

  ERL_DRV_FLOAT       : ErlDrvTermData = 11; // double *
  ERL_DRV_EXT2TERM    : ErlDrvTermData = 12; // char *, ErlDrvUInt
  ERL_DRV_UINT        : ErlDrvTermData = 13; // ErlDrvUInt
  ERL_DRV_BUF2BINARY  : ErlDrvTermData = 14; // char *, ErlDrvUInt


(*
 * This macro is used to name a dynamic driver's init function in
 * a way that doesn't lead to conflicts. This is crucial when using
 * operating systems that has one namespace for all symbols
 * (e.g. VxWorks). Example: if you have an dynamic driver C source
 * file named echo_drv.c, you use the macro like this:
 *
 *    DRIVER_INIT(echo_drv)
 *    {
 *   ....
 *    }
 *
 * This function well be called by the Erlang I/O system when the driver is loaded.
 * It must initialize a ErlDrvEntry structure and return a pointer to it.
 *)

{$IFNDEF ERL_DRIVER_TYPES_ONLY}
  {$I !ERL_DRIVER_TYPES_ONLY.INC}
{$ENDIF}
var
(*
 * These are the functions available for driver writers.
 *)
  driver_select        : function (port: ErlDrvPort; event: ErlDrvEvent; mode: Integer; on_: Integer): Integer; cdecl;
  driver_event         : function (port: ErlDrvPort; event: ErlDrvEvent; event_data: ErlDrvEventData): Integer; cdecl;
  driver_output        : function (port: ErlDrvPort; buf: PAnsiChar; len: Integer): Integer; cdecl;
  driver_output2       : function (port: ErlDrvPort; hbuf: PAnsiChar; hlen: Integer; buf: PAnsiChar; len: Integer): Integer; cdecl;
  driver_output_binary : function (port: ErlDrvPort; hbuf: PAnsiChar; hlen: Integer; bin: PErlDrvBinary; offset: Integer; len: Integer): Integer; cdecl;
  driver_outputv       : function (port: ErlDrvPort; hbuf: PAnsiChar; hlen: Integer; ev: PErlIOVec; skip: Integer): Integer; cdecl;
  driver_vec_to_buf    : function (ev: PErlIOVec; buf: PAnsiChar; len: Integer): Integer; cdecl;
  driver_set_timer     : function (port: ErlDrvPort; time: Cardinal): Integer; cdecl;
  driver_cancel_timer  : function (port: ErlDrvPort): Integer; cdecl;
  driver_read_timer    : function (port: ErlDrvPort; time_left: PCardinal): Integer; cdecl;

(*
 * Get plain-text error message from within a driver
 *)
  erl_errno_id: function (error: Integer): PAnsiChar; cdecl;

(*
 * The following functions are used to initiate a close of a port
 * from a driver.
 *)
  driver_failure_eof  : function (port: ErlDrvPort): Integer; cdecl;
  driver_failure_atom : function (port: ErlDrvPort; str: PAnsiChar): Integer; cdecl;
  driver_failure_posix: function (port: ErlDrvPort; error: Integer): Integer; cdecl;
  driver_failure      : function (port: ErlDrvPort; error: Integer): Integer; cdecl;
  driver_exit         : function (port: ErlDrvPort; err: Integer): Integer; cdecl;

(*
 * Port Data Lock
 *)
  driver_pdl_create  : function (port: ErlDrvPort): ErlDrvPDL; cdecl;
  driver_pdl_lock    : procedure(pdl: ErlDrvPDL); cdecl;
  driver_pdl_unlock  : procedure(pdl: ErlDrvPDL); cdecl;
  driver_pdl_get_refc: function (pdl: ErlDrvPDL): LongInt; cdecl;
  driver_pdl_inc_refc: function (pdl: ErlDrvPDL): LongInt; cdecl;
  driver_pdl_dec_refc: function (pdl: ErlDrvPDL): LongInt; cdecl;

(*
 * Process monitors
 *)
  driver_monitor_process       : function (port: ErlDrvPort; process: ErlDrvTermData; monitor: PErlDrvMonitor): Integer; cdecl;
  driver_demonitor_process     : function (port: ErlDrvPort; const monitor: PErlDrvMonitor): Integer; cdecl;
  driver_get_monitored_process : function (port: ErlDrvPort; const monitor: PErlDrvMonitor): ErlDrvTermData; cdecl;
  driver_compare_monitors      : function (const monitor1: PErlDrvMonitor; const monitor2: PErlDrvMonitor): Integer; cdecl;

(*
 * Port attributes
 *)
  set_busy_port          : procedure(port: ErlDrvPort; on_: Integer ); cdecl;
  set_port_control_flags : procedure(port: ErlDrvPort; flags: Integer ); cdecl;
  get_port_flags         : function (port: ErlDrvPort): Integer; cdecl;


(* Binary interface *)


(*
 * NOTE: DO NOT overwrite a binary with new data (if the data is delivered);
 * since the binary is a shared object it MUST be written once.
 *)
  driver_alloc_binary  : function (size: Integer): PErlDrvBinary; cdecl;
  driver_realloc_binary: function (bin: PErlDrvBinary; size: Integer): PErlDrvBinary; cdecl;
  driver_free_binary   : procedure(bin: PErlDrvBinary); cdecl;

(* Referenc count on driver binaries *)
  driver_binary_get_refc: function (dbp: PErlDrvBinary): LongInt; cdecl;
  driver_binary_inc_refc: function (dbp: PErlDrvBinary): LongInt; cdecl;
  driver_binary_dec_refc: function (dbp: PErlDrvBinary): LongInt; cdecl;

(* Allocation interface *)
  driver_alloc  : function (size: size_t): Pointer; cdecl;
  driver_realloc: function (ptr: Pointer; size: size_t): Pointer; cdecl;
  driver_free   : procedure(ptr: Pointer); cdecl;

(* Queue interface *)
  driver_enq      : function (port: ErlDrvPort; buf: PAnsiChar; len: Integer): Integer; cdecl;
  driver_pushq    : function (port: ErlDrvPort; buf: PAnsiChar; len: Integer): Integer; cdecl;
  driver_deq      : function (port: ErlDrvPort; size: Integer ): Integer; cdecl;
  driver_sizeq    : function (port: ErlDrvPort): Integer; cdecl;
  driver_enq_bin  : function (port: ErlDrvPort; bin: PErlDrvBinary; offset,len: Integer): Integer; cdecl;
  driver_pushq_bin: function (port: ErlDrvPort; bin: PErlDrvBinary; offset,len: Integer): Integer; cdecl;

  driver_peekqv   : function (port: ErlDrvPort; ev: PErlIOVec ): Integer; cdecl;
  driver_peekq    : function (port: ErlDrvPort; vlen: PInteger ): PSysIOVec; cdecl;
  driver_enqv     : function (port: ErlDrvPort; ev: PErlIOVec; skip: Integer): Integer; cdecl;
  driver_pushqv   : function (port: ErlDrvPort; ev: PErlIOVec; skip: Integer): Integer; cdecl;

(*
 * Add and remove driver entries.
 *)
  add_driver_entry   : procedure(de: PErlDrvEntry); cdecl;
  remove_driver_entry: function (de: PErlDrvEntry): Integer; cdecl;

(*
 * System info
 *)
  driver_system_info : procedure(sip: PErlDrvSysInfo; si_size: size_t ); cdecl;

(*
 * erl driver thread functions.
 *)
  erl_drv_mutex_create    : function (name: PAnsiChar): PErlDrvMutex; cdecl;
  erl_drv_mutex_destroy   : procedure(mtx: PErlDrvMutex); cdecl;
  erl_drv_mutex_trylock   : function (mtx: PErlDrvMutex): Integer; cdecl;
  erl_drv_mutex_lock      : procedure(mtx: PErlDrvMutex); cdecl;
  erl_drv_mutex_unlock    : procedure(mtx: PErlDrvMutex); cdecl;

  erl_drv_cond_create     : function (name: PAnsiChar): PErlDrvCond; cdecl;
  erl_drv_cond_destroy    : procedure(cnd: PErlDrvCond); cdecl;
  erl_drv_cond_signal     : procedure(cnd: PErlDrvCond); cdecl;
  erl_drv_cond_broadcast  : procedure(cnd: PErlDrvCond); cdecl;
  erl_drv_cond_wait       : procedure(cnd: PErlDrvCond; mtx: PErlDrvMutex ); cdecl;

  erl_drv_rwlock_create   : function (name: PAnsiChar): PErlDrvRWLock; cdecl;
  erl_drv_rwlock_destroy  : procedure(rwlck: PErlDrvRWLock); cdecl;
  erl_drv_rwlock_tryrlock : function (rwlck: PErlDrvRWLock): Integer; cdecl;
  erl_drv_rwlock_rlock    : procedure(rwlck: PErlDrvRWLock); cdecl;
  erl_drv_rwlock_runlock  : procedure(rwlck: PErlDrvRWLock); cdecl;
  erl_drv_rwlock_tryrwlock: function (rwlck: PErlDrvRWLock): Integer; cdecl;
  erl_drv_rwlock_rwlock   : procedure(rwlck: PErlDrvRWLock); cdecl;
  erl_drv_rwlock_rwunlock : procedure(rwlck: PErlDrvRWLock); cdecl;

  erl_drv_tsd_key_create  : function (name: PAnsiChar; key: PErlDrvTSDKey): Integer; cdecl;
  erl_drv_tsd_key_destroy : procedure(key: ErlDrvTSDKey); cdecl;
  erl_drv_tsd_set         : procedure(key: ErlDrvTSDKey; data: Pointer); cdecl;
  erl_drv_tsd_get         : function (key: ErlDrvTSDKey): Pointer; cdecl;

  erl_drv_thread_opts_create   : function (name: PAnsiChar): PErlDrvThreadOpts; cdecl;
  erl_drv_thread_opts_destroy  : procedure(opts: PErlDrvThreadOpts); cdecl;
  erl_drv_thread_create        : function (name: PAnsiChar; tid: PErlDrvTid; func: TThreadFunc; args: Pointer; opts:PErlDrvThreadOpts):Integer; cdecl;
  erl_drv_thread_self          : function (): ErlDrvTid; cdecl;
  erl_drv_equal_tids           : function (tid1: ErlDrvTid; tid2: ErlDrvTid ): Integer; cdecl;
  erl_drv_thread_exit          : procedure(resp: Pointer); cdecl;
  erl_drv_thread_join          : function (tid: ErlDrvTid; respp: PPointer): Integer; cdecl;

(*
 * Misc.
 *)
  null_func : function: Integer; cdecl;

(*
 * terms.
 *)

(* make terms for driver_output_term and driver_send_term *)
  driver_mk_atom     : function (name: PAnsiChar): ErlDrvTermData; cdecl;
  driver_mk_port     : function (port: ErlDrvPort): ErlDrvTermData; cdecl;
  driver_connected   : function (port: ErlDrvPort): ErlDrvTermData; cdecl;
  driver_caller      : function (port: ErlDrvPort): ErlDrvTermData; cdecl;
  driver_mk_term_nil : function: ErlDrvTermData; cdecl;

(* output term data to the port owner *)
  driver_output_term: function (ix: ErlDrvPort; data: PErlDrvTermData; len: Integer): Integer; cdecl;
(* output term data to a specific process *)
  driver_send_term: function (ix: ErlDrvPort; to_: ErlDrvTermData; data: PErlDrvTermData; len: Integer): Integer; cdecl;

(* Async IO functions *)
type
  T_Async_Invoke = procedure(p: Pointer);
  T_Async_Free = procedure(p: Pointer);

var
  driver_async : function (ix: ErlDrvPort; key: PCardinal;
    async_invoke: T_Async_Invoke;
    async_data: Pointer;
    async_free: T_Async_Free): LongInt; cdecl;

  driver_async_cancel: function (key: PCardinal): Integer; cdecl;

(* Locks the driver in the machine "forever",there is
   no unlock function. Note that this is almost never useful,as an open
   port towards the driver locks it until the port is closed,why unexpected
   unloading "never" happens. *)
  driver_lock_driver : function (ix: ErlDrvPort): Integer; cdecl;

(* Get the current 'now' timestamp (analogue to erlang:now()) *)
  driver_get_now     : function (now: PErlDrvNowData): Integer; cdecl;

(* These were removed from the ANSI version,now they're back. *)
  driver_dl_open     : function (ddll: PAnsiChar): Pointer; cdecl;
  driver_dl_sym      : function (lib_handle: Pointer; name: PAnsiChar): Pointer; cdecl;
  driver_dl_close    : function (lib_handle: Pointer): Integer; cdecl;
  driver_dl_error    :  function: PAnsiChar; cdecl;

(* environment *)
  erl_drv_putenv     : function (key, value: PAnsiChar): Integer; cdecl;
  erl_drv_getenv     : function (key, value: PAnsiChar; value_size: p_size_t): Integer; cdecl;

var
  DRIVER_INIT_BY_USER : function(DrvName: PAnsiChar): PErlDrvEntry;

function driver_term_nil: ErlDrvTermData;
{* can't found in callbacks!
function driver_create_port(creator_port: ErlDrvPort; connected: ErlDrvTermData; name:PAnsiChar; drv_data: ErlDrvData): ErlDrvPort;
*}

implementation

(* For windows dynamic drivers *)
{$IFDEF WIN32_DYNAMIC_ERL_DRIVER}

{$I ErlCallbackTypes.inc}

{$IFDEF USE_REAL_DRIVER_NAME}
function GetModuleFileNameA(hModule: HINST; lpFilename: PAnsiChar; nSize: Cardinal): Cardinal; stdcall; external 'kernel32.dll' name 'GetModuleFileNameA';

const
  c1 : AnsiChar = '\';
  c2 : AnsiChar = '.';

var
  real_module_name : AnsiString;

function erl_dyndriver_real_driver_init: PErlDrvEntry;
var
  I : Integer;
  S : AnsiString;
begin
  // get the dll's module name
  SetLength(S, 1024);
  SetLength(S, GetModuleFileNameA(HInstance, @S[1], 1024));

  //  real_module_name := ExtractFileName(S);
  I := pos(c1, S);
  while I>0 do
  begin
    delete(S, 1, I);
    I := pos(c1, S);
  end;

  // real_module_name := ChangeFileExt(real_module_name, '');
  I := pos(c2, S);
  real_module_name := '';
  while I>0 do
  begin
    real_module_name := real_module_name + copy(S, 1, I);
    delete(S, 1, I);
    I := pos(c2, S);
  end;
  SetLength(real_module_name, length(real_module_name)-1);

  Result := DRIVER_INIT_BY_USER(@real_module_name[1]);
end;
{$ELSE}
function erl_dyndriver_real_driver_init: PErlDrvEntry;
begin
  Result := DRIVER_INIT_BY_USER(nil);
end;
{$ENDIF}

function driver_init(callbacks: PWinDynDriverCallbacks): PErlDrvEntry; cdecl;
begin
  get_cllback_entries(callbacks);  // defined in ErlCallbackTypes.inc
  Result := erl_dyndriver_real_driver_init;
end;

(* unknow function/procedures in Callbacks
erts_alc_test
*)
{$ENDIF}

// The only variable in the interface...
// #define driver_term_nil (driver_mk_term_nil())
function  driver_term_nil: ErlDrvTermData;
begin
  Result := driver_mk_term_nil;
end;

exports
  driver_init;

end.
